#pragma once
#include <iostream>
#include <string>
#include <cstring>
#include <sstream>
#include <filesystem> // 让编译器知道filesystem是std的嫡系部队
#include <fstream>
#include <memory>
#include <time.h>
#include <unistd.h>
#include "Mutex.hpp"
#include <bits/fs_fwd.h>
// 这个文件主要是来进行日志的书写,比较麻烦，要结合课件来写

// 先设置好一个命名空间域，表示是我们自己设置的log
namespace LogModule
{
    using namespace MutexModule;
    std ::string gsep = "\r\n"; // 回车并且换行，就相当于C中的\n
    // 策略模式：c++的多态特性
    // 2.刷新策略：1.往显示器打印；2.往固定的文件打印
    class LogStratey
    {
    public:
        ~LogStratey() = default;
        virtual void Synclog(const std ::string &message) = 0; // 具体的多态用法还是得复习C++才知道。
    };
    // 显示器策略的打印：子类
    class ConsoleLogStrategy : public LogStratey // 继承的特性
    {
    public:
        ConsoleLogStrategy() {}
        void Synclog(const std ::string &message) override
        {
            LockGurd s1(_mutex);
            std ::cout << message << gsep;
        }

    private:
        Mutex _mutex;
    };

    std ::string defaultpath = "./log";   // 这个是显示文件所在的路径
    std ::string defaultfile = "log.txt"; // 这个是显示文件名

    class FileLogStrategy : public LogStratey
    {
    public:
        FileLogStrategy(const std ::string &path = defaultpath, const std ::string &file = defaultfile)
            : _path(path),
              _file(file)
        {
            LockGurd s1(_mutex);
            if (std::filesystem::exists(_path))
            {
                return;
            }
            try
            {
                std ::filesystem::create_directories(_path);
            }
            catch (const std::filesystem::filesystem_error &e)
            {
                std::cerr << e.what() << '\n';
            }
        }
        void Synclog(const std ::string &message) override
        {
            LockGurd s1(_mutex);
            std::string filename = _path + (_path.back() == '/' ? "" : "/") + _file; // "./log/" + "my.log"
            std::ofstream out(filename, std::ios::app);                              // 追加写入的 方式打开
            if (!out.is_open())
            {
                return;
            }
            out << message << gsep;
            out.close();
        }

    private:
        std ::string _path; // 文件所在的路径
        std ::string _file; // 文件的名字
        Mutex _mutex;       // 保护临界资源
    };

    // 形成一个完整的日志需要上面的策略模式，根据不同的策略生成不同的日志

    // 日志也是有等级的
    enum class LogLeval
    {
        DEBUG,
        INFO,
        WARNING,
        ERROR,
        FATAL
    };

    std ::string Leval125tr(LogLeval leval)
    {
        switch (leval)
        {
        case LogLeval ::DEBUG:
            return "DEBUG";
        case LogLeval ::INFO:
            return "INFO";
        case LogLeval ::WARNING:
            return "WARING";
        case LogLeval ::ERROR:
            return "ERROR";
        case LogLeval ::FATAL:
            return "FATAL";
        default:
            return nullptr;
        } // 通过选择不同的模式来选择日志的等级
    }

    std::string GetTimeStamp()
    {
        time_t curr = time(nullptr);
        struct tm curr_tm;
        localtime_r(&curr, &curr_tm);
        char timebuffer[128];
        snprintf(timebuffer, sizeof(timebuffer), "%4d-%02d-%02d %02d:%02d:%02d",
                 curr_tm.tm_year + 1900,
                 curr_tm.tm_mon + 1,
                 curr_tm.tm_mday,
                 curr_tm.tm_hour,
                 curr_tm.tm_min,
                 curr_tm.tm_sec);
        return timebuffer;
    }

    // 1.形成日志；2.根据不同的刷新策略，刷新日志
    // 外部类表示刷新策略，内部类表示的是一挑完整的日志信息
    class Logger
    {
    public:
        Logger()
        {
            EnableConsoleLogStrategy();
        }

        void EnableFileLogStrategy()
        {
            _fflush_strategy = std ::make_unique<FileLogStrategy>();
        }
        void EnableConsoleLogStrategy()
        {
            _fflush_strategy = std ::make_unique<ConsoleLogStrategy>();
        }
        // 里面存放的是一条完整的日志信息，用内部类是最方便的方式
        class Logmessage
        {
        public:
            Logmessage(LogLeval &leval, std ::string src_name, int line_number, Logger &logger)
                : _curr_time(GetTimeStamp()),
                  _leval(leval),
                  _pid(getpid()),
                  _src_name(src_name),
                  _line_number(line_number),
                  _logger(logger)
            {
                std ::stringstream ss;
                ss << "[" << _curr_time << "]"
                   << "[" << Leval125tr(_leval) << "]"
                   << "[" << _pid << "]"
                   << "[" << src_name << "]"
                   << "[" << _line_number << "]"
                   << "-";
                _loginfo = ss.str();
            }
            template <class T>
            Logmessage &operator<<(const T &in)
            {
                // 这个日志最右边的参数是可变的，所以此时需要对运算符进行重载
                std ::stringstream ss;
                ss << in;
                _loginfo += ss.str();
                return *this;
            }
            ~Logmessage()
            {
                if (_logger._fflush_strategy)
                {
                    _logger._fflush_strategy->Synclog(_loginfo); // 把里面的东西刷新到外面去
                }
            }

        private:
            std ::string _curr_time; // 时间
            LogLeval _leval;         // 日志的等级
            pid_t _pid;              // 进程的id
            std ::string _src_name;  // 文件的名字
            int _line_number;        // 行号
            std ::string _loginfo;   // 合并后的一条完整信息
            Logger &_logger;         // 存放的是外部的类，其实就是刷新策略
        };
        ~Logger()
        {
        }
        // 通过仿函数的方式对于Logmessage进行调用
        Logmessage operator()(LogLeval leval, std ::string name, int line)
        {
            return Logmessage(leval, name, line, *this);
        }

    private:
        std::unique_ptr<LogStratey> _fflush_strategy;
    };
    Logger logger;
// 使用宏，简化用户操作，获取文件名和行号
#define LOG(level) logger(level, __FILE__, __LINE__)
#define Enable_Console_Log_Strategy() logger.EnableConsoleLogStrategy()
#define Enable_File_Log_Strategy() logger.EnableFileLogStrategy()
}